/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.epidemy.actors

import scala.actors._

object Ticker extends Actor {
  private class Appointment(val time: Long, val action: () => Unit)

  private var appointments = List[Appointment]()
  
  private def appointment(time: Long)(action: => Unit) = {
    val appointment = new Appointment(time, () => action)
    this.synchronized {
      var needsInterrupt = appointments.isEmpty || appointments.head.time > appointment.time
      appointments = (appointment :: appointments).sortWith((a, b) => a.time < b.time)
      if (needsInterrupt)
        thread.interrupt
    }
            
  }
  private val thread = new Thread {
    override def run = {
      while (true) {
                
        val next = Ticker.this.synchronized {
          Thread.interrupted
          appointments.headOption.map(_.time).getOrElse(Long.MaxValue)//System.currentTimeMillis + 100 * 1000)
        }
        val now = System.currentTimeMillis
                
        val time = next - now
        //println("Sleeping for " + time + " milliseconds...")
        try { Thread.sleep(time) } catch { case _ =>
        //  println("interrupted")
        }
        val appointment = Ticker.this.synchronized {
          appointments match {
            case Nil => None
            case a :: rest => 
              if (now - a.time <= tolerance) {
                appointments = rest
                Some(a)
              } else
                None
          }
        }
        if (appointment != None)
          appointment.get.action()
      }
    }
  }
    
  private var tolerance = 10 // ms
  def act = {
    thread.start
    loop {
      react {
        case Every(delay, msg, recipient) => { 
            val d = if (recipient == null) sender else recipient
            def schedule: Unit = 
              appointment(System.currentTimeMillis + delay.millis) {
                schedule
                d ! msg
              }
            schedule
          }
        case In(delay, msg, recipient) => {
            val d = if (recipient == null) sender else recipient
            appointment(System.currentTimeMillis + delay.millis) {
              d ! msg
            }
          }
        case m => println("Ticker received " + m)
      }
    }
  }
  //start

  implicit def int2ts(value: Int) = new TimeLiteral(value)
  implicit def long2ts(value: Long) = new TimeLiteral(value)
}

case class TimeLiteral(value: Long) {
  def millis = new Timespan(value)
  def milliseconds = millis
  def seconds = new Timespan(value * 1000) 
  def minutes = new Timespan(value * 1000 * 60)
  def hours = new Timespan(value * 1000 * 60 * 60)
}
case class Timespan(millis: Long) {   
  def and(other: Timespan) = new Timespan(millis + other.millis)
}
case class In(timespan: Timespan, msg: Any, recipient: Actor) {
  def this(timespan: Timespan, msg: Any) = this(timespan, msg, null)
}

case class Every(timespan: Timespan, msg: Any, recipient: Actor) {
  def this(timespan: Timespan, msg: Any) = this(timespan, msg, null)
}

